package com.zb.aop;

import com.zb.annotation.RequestLimit;
import com.zb.result.Result;
import com.zb.result.ResultCode;
import com.zb.utils.BrowserUtil;
import com.zb.utils.RequestUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * 请求接口限制aop
 * @Author WEN
 * @Date 2023/8/29 0029
 **/
@Aspect
@Component
public class RequestLimitAspect {
    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 接口限流redis的key值
     */
    private String REDIS_KEY_INITERFACE_LIMIT = "zb:interface-limit:";

    /**
     * 正常接口默认每分钟请求次数
     */
    private int LIMIT_CONT = 60;

    /**
     * 限制时间，单位：秒，默认时间为60s
     */
    private long timout = 60;

    /**
     * 全局限流
     */
    @Pointcut("execution(public * com.zb.*.controller.*Controller.*(..))")
    public void controller() {}

    @Pointcut("execution(public * com.zb.mobile.*.controller.*Controller.*(..))")
    public void mobileController() {}


    /**
     * 全局拦截，排除使用注解限流、登录方法
     */
    @Around("(controller() || mobileController()) && !com.zb.log.PCLoginLogAspect.PCLoginLogAspect()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        HttpServletRequest request = RequestUtil.getRequestByContext();
        String ip = BrowserUtil.getIpAddress(request);
        String uri = request.getRequestURI();
        String redisKey = REDIS_KEY_INITERFACE_LIMIT.concat(ip).concat(uri);

        // 获取方法注释
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        RequestLimit requestLimit = method.getAnnotation(RequestLimit.class);

        ZSetOperations zSetOperations = redisTemplate.opsForZSet();
        long currentMs = System.currentTimeMillis();
        zSetOperations.add(redisKey, currentMs, currentMs);
        Long count = zSetOperations.zCard(redisKey);
        return joinPoint.proceed();

        // 接口限流注解
//        if (null != requestLimit) {
//            redisTemplate.expire(redisKey, requestLimit.timeout(), TimeUnit.SECONDS);
//            zSetOperations.removeRangeByScore(redisKey, 0, currentMs - requestLimit.timeout() * 1000);
//            if (count <= requestLimit.count()) {
//                return joinPoint.proceed();
//            }
//        } else {
//            redisTemplate.expire(redisKey, timout, TimeUnit.SECONDS);
//            zSetOperations.removeRangeByScore(redisKey, 0, currentMs - timout * 1000);
//            if (count <= LIMIT_CONT) {
//                return joinPoint.proceed();
//            }
//        }
//        return Result.error(ResultCode.REQUEST_INTERFACE_LIMIT.getCode(), ResultCode.REQUEST_INTERFACE_LIMIT.getMessage());
    }

}
